<?php

declare(strict_types=1);
/**
 * This file is part of habo_new_media.
 *
 * @link     https://www.berlen.cn/
 * @contact  tech@berlen.cn
 * @license  http://gitlab.rehou.cn/habo/habo_new_media/-/blob/master/README.md
 */

namespace app\service\notice\qywx;

use app\enum\NoticeEnum;
use app\enum\QywxEnum;
use app\enum\QywxUrlEnum;
use app\model\StaffModel;
use app\model\WechatUserModel;
use app\service\BaseService;
use app\service\notice\NoticeInterface;
use app\service\notice\NoticeLogTrait;
use proxy\Client;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\Exception;
use think\facade\Cache;

final class QywxService extends BaseService implements NoticeInterface
{
    use NoticeLogTrait;

    /**
     * @var string 企业id
     */
    private const CORPID = 'wwe5e95925d980930f';

    /**
     * @var string 应用密钥
     */
    private const SECRET = 'iR-_Gwd9cKNXNpMANmr3Df6IXOSeRKlSfgRTfi2jKD4';

    /**
     * @var array 请求参数
     */
    protected array $req = [];

    /**
     * @var array 应答参数
     */
    protected array $rep = [];

    /**
     * @var array 接收人
     */
    protected array $staffs = [];

    /**
     * @var int 通知类型
     */
    protected int $noticeType = NoticeEnum::NOTICE_TYPE_QYWX;

    /**
     * @var array 配置
     */
    private array $conf = [];

    /**
     * @var string token
     */
    private string $token = '';

    public function __construct(array $conf = [])
    {
        $this->setConf($conf);
        $this->setToken();
    }

    /**
     * 获取 tenant_access_token（企业自建应用）.
     * @return bool|mixed
     * @throws Exception
     */
    public function getAccessToken()
    {
        $params = $this->getParam();
        $redis = Cache::store('redis')->handler();
        $client = new Client();
        $serviceName = QywxUrlEnum::getServiceName('qywx');
        $serviceUrl = QywxUrlEnum::getServiceUrl('token', 'access_token') . '?corpid=' . self::CORPID . '&corpsecret=' . self::SECRET;
        $data = $client->get($serviceName, $serviceUrl, $params)->exec(Client::RETURN_TYPE);
        if (! $data) {
            return self::setError('获取企业微信token失败');
        }
        $expire = $data['expires_in'] - 120;
        $redis->set(QywxEnum::TOKEN_KEY, $data['access_token'], $expire);
        return $data['access_token'];
    }

    /**
     * 获取单个用户的信息.
     * @param int $phone 手机号
     * @return bool|mixed|string
     * @throws Exception
     */
    public function getUserInfo(int $phone = 0)
    {
        $client = new Client();
        $serviceName = QywxUrlEnum::getServiceName('qywx');
        $serviceUrl = QywxUrlEnum::getServiceUrl('msg', 'user_id') . "?access_token={$this->token}";
        $data = [
            'mobile' => $phone,
        ];
        return $client->postJson($serviceName, $serviceUrl, $data)->exec(Client::RETURN_TYPE);
    }

    /**
     * 推送消息.
     * @throws Exception
     */
    public function sendMessages(array $data = []): bool
    {
        $this->req = $data;
        $headers = self::getHeader();
        $client = new Client();
        $serviceName = QywxUrlEnum::getServiceName('qywx');
        $serviceUrl = QywxUrlEnum::getServiceUrl('msg', 'send_messages') . "?access_token={$this->token}";
        $this->rep = $client->postJson($serviceName, $serviceUrl, $this->req)->setHeader($headers)->exec(Client::RETURN_TYPE);
        if (! $this->setNoticeLog()) {
            return false;
        }
        return $this->isSendSuccess() || self::setError($res['msg'] ?? '发送失败');
    }

    /**
     * 获取企业成员id.
     * @param array $staffIds [员工id]
     * @throws DataNotFoundException
     * @throws DbException
     * @throws Exception
     * @throws ModelNotFoundException
     */
    public function getMember(array $staffIds): string
    {
        $memberIds = [];
        foreach ($staffIds as $v) {
            $wechatModel = (new WechatUserModel());
            $wxChatData = $wechatModel->where('staff_id', $v)->find();
            if ($wxChatData) {
                $memberIds[] = $wxChatData['user_id'];
            } else {
                $phone = (new StaffModel())->where('id', $v)->value('mobile');
                if ($phone) {
                    $userInfo = self::getUserInfo(intval($phone));
                    if ($userInfo && $userInfo['errcode'] == 0) {
                        $memberIds[] = $userInfo['userid'];
                        $data['staff_id'] = $v;
                        $data['mobile'] = $phone;
                        $data['user_id'] = $userInfo['userid'];
                        $wechatModel->create($data);
                    }
                }
            }
        }
        return implode('|', $memberIds);
    }

    /**
     * 添加公共参数.
     * @param array $params 请求参数
     */
    public function getParam(array $params = []): array
    {
        return $params;
    }

    /**
     * 设置配置.
     * @param array $conf 配置
     */
    public function setConf(array $conf = []): bool
    {
        $this->conf = $conf;
        return true;
    }

    /**
     * 发送单个消息.
     * @param array $params 请求参数
     * @return true
     */
    public function sendSingleMessage(array $params = []): bool
    {
        return true;
    }

    /**
     * 推送消息.
     * @param array $params 请求参数
     * @throws Exception
     */
    public function sendBatchMessage(array $params = []): bool
    {
        $this->req = $params;
        $headers = self::getHeader();
        $client = new Client();
        $serviceName = QywxUrlEnum::getServiceName('qywx');
        $serviceUrl = QywxUrlEnum::getServiceUrl('msg', 'send_messages') . "?access_token={$this->token}";
        $this->rep = $client->postJson($serviceName, $serviceUrl, $this->req)->setHeader($headers)->exec(Client::RETURN_TYPE);
        if (! $this->setNoticeLog()) {
            return false;
        }
        return $this->isSendSuccess() || self::setError($res['msg'] ?? '发送失败');
    }

    /**
     * 是否发送成功.
     */
    public function isSendSuccess(): bool
    {
        return isset($this->rep['errcode']) && $this->rep['errcode'] == 0;
    }

    /**
     * 设置header.
     * @return string[]
     */
    private function getHeader(): array
    {
        return [
            'Authorization' => 'Bearer ' . $this->token,
            'Content-Type' => 'application/json; charset=utf-8',
        ];
    }

    /**
     * 设置token方法.
     * @throws Exception
     */
    private function setToken(): bool
    {
        $redis = Cache::store('redis')->handler();
        $redisKey = $redis->get(QywxEnum::TOKEN_KEY);
        if (! $redisKey) {
            $this->token = self::getAccessToken();
        }
        $this->token = $redis->get(QywxEnum::TOKEN_KEY);
        return true;
    }
}
